package com.nsharmon.model.transformer;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.nsharmon.dao.entity.Axiom;
import com.nsharmon.dao.entity.ComparisonAxiom.BlankThanVariant;
import com.nsharmon.dao.entity.EntityObject;
import com.nsharmon.dao.entity.ComparisonAxiom;
import com.nsharmon.dao.entity.EqualityAxiom;
import com.nsharmon.dao.entity.ComparisonAxiom.LessThanVariant;
import com.nsharmon.dao.entity.Parameter;

public class ComparisonTransformer implements Transformer {
	public boolean transformBegin() {
		return true;
	}

	@Override
	public Set<Axiom> transform(Set<Axiom> axioms) {
		Set<Axiom> generatedAxioms = new LinkedHashSet<Axiom>();
		
		// Search for extra relationships
		Iterator<Axiom> it = axioms.iterator();
		while(it.hasNext()) {
			Axiom axiom = it.next();
			if(axiom instanceof ComparisonAxiom) {
				ComparisonAxiom rel = (ComparisonAxiom)axiom;
				
				if(rel.getVariant() != BlankThanVariant.AsBlankAs) {
					applyBlankThanComparison(axioms, generatedAxioms, rel);
				} else {
					applyAsBlankAsComparison(axioms, generatedAxioms, rel);
				}
			}
		}
		return generatedAxioms;
	}

	public Set<Axiom> transformEnd( Set<Axiom> axioms ) {
		return null;
	}
	
	
	private void applyBlankThanComparison( Set<Axiom> axioms,
			Set<Axiom> generatedAxioms, ComparisonAxiom rel ) {
		Set<ComparisonAxiom> comparisonAxioms = getAxiomsByLesser(axioms, rel.getGreater());
		for(ComparisonAxiom comparisonLink : comparisonAxioms) {
			Enum<?> variant = getVariant(comparisonLink, rel);
			
			// If custom comparator, be sure they match before generating conclusions
			if(rel.getParameters().equals(comparisonLink.getParameters())) {
				ComparisonAxiom ca = new ComparisonAxiom();
				ca.setGreater(comparisonLink.getGreater());
				ca.setLesser(rel.getLesser());
				ca.setParameters(getParameters(comparisonLink, rel));
				ca.setVariant(variant);
				
				if(!axioms.contains(ca)) {
					generatedAxioms.add(ca);
				}
			}
		}
	}

	public void applyAsBlankAsComparison(Set<Axiom> axioms,
			Set<Axiom> generatedAxioms, ComparisonAxiom ea) {
		for(EntityObject eo : ea.getEntities()) {
			Set<Axiom> nonAsBlankAsComparisonAxioms = getNonAsBlankAsComparisonAxiomsByEntity(axioms, eo, ea.getParameters());
			
			for(Axiom nonEqualityAxiom : nonAsBlankAsComparisonAxioms) {
				for(EntityObject eo2 : ea.getEntities()) {
					if(!eo.equals(eo2)) {
						Axiom clonedAxiom = nonEqualityAxiom.clone();
						clonedAxiom.replace(eo, eo2);
						
						if(!axioms.contains(clonedAxiom)) {
							generatedAxioms.add(clonedAxiom);
						}
					}
				}
			}
		}
	}
	
	private static Set<Axiom> getNonAsBlankAsComparisonAxiomsByEntity(Set<Axiom> axioms, EntityObject eo, List<Parameter> parameters) {
		Set<Axiom> nonEqualityAxioms = new LinkedHashSet<Axiom>();
		for(Axiom axiom : axioms) {
			if((axiom instanceof ComparisonAxiom) && axiom.getVariant() != BlankThanVariant.AsBlankAs && axiom.getParameters().equals(parameters)) {
				List<EntityObject> entities = axiom.getEntities();
				for(EntityObject eoAxiom : entities) {
					if(eoAxiom.equals(eo)) {
						nonEqualityAxioms.add(axiom);
					}
				}
			}
		}
		return nonEqualityAxioms;
	}
	
	private List<Parameter> getParameters(ComparisonAxiom comparisonLink, ComparisonAxiom rel) {
		List<Parameter> params = new ArrayList<Parameter>(comparisonLink.getParameters());
		params.addAll(rel.getParameters());

		return params;
	}
	
	private Enum<?> getVariant(ComparisonAxiom comparisonLink, ComparisonAxiom rel) {
		// If A <= B and B <= C, then A <= C.  Otherwise, A < C
		BlankThanVariant variant = BlankThanVariant.IsBlankThan;
		if (comparisonLink.getVariant() == BlankThanVariant.IsBlankThanOrEqualTo && rel.getVariant() == BlankThanVariant.IsBlankThanOrEqualTo) {
			variant = BlankThanVariant.IsBlankThanOrEqualTo;
		}
		return variant;
	}

	private Set<ComparisonAxiom> getAxiomsByLesser(Set<Axiom> axioms, EntityObject lesser) {
		Set<ComparisonAxiom> comparisonAxioms = new LinkedHashSet<ComparisonAxiom>();
		for(Axiom axiom : axioms) {
			if(axiom instanceof ComparisonAxiom) {
				ComparisonAxiom ca = (ComparisonAxiom)axiom;
				EntityObject containedEntityObject = ca.getLesser();
				if(containedEntityObject.equals(lesser)) {
					comparisonAxioms.add(ca);
				}
			}
		}
		return comparisonAxioms;
	}
}
